<template>
  <div
    :class="{ 'tool-tip-box': true, 'tool-tip-dark': effect !== 'light' }"
    name="toolTip"
  >
    <!-- 插槽原内容 @mouseenter="mouseEnter" @mouseleave="mouseLeave" -->
    <div ref="toolTipParent">
      <slot />
    </div>
    <!-- 提示的DOM -->
    <div
      :class="{ 'tool-tip-dom': true, 'tool-tip-dom-display': displayToolTip }"
      ref="toolTipDom"
      @mouseenter="mouseEnterTip"
      @mouseleave="mouseLeaveTip"
    >
      <!-- 内容 -->
      <div :class="['tool-tip-content', placementPosition]">{{ content }}</div>
      <!-- 三角 -->
      <div :class="['tip-arrow', placementPosition]" />
    </div>
  </div>
</template>

<script>
import Vue from 'vue';
/* istanbul ignore next */
const on = (function () {
  if (
    !(Vue.prototype.$isServer || Number(document.documentMode)) &&
    document.addEventListener
  ) {
    return function (element, event, handler) {
      if (element && event && handler) {
        element.addEventListener(event, handler, false);
      }
    };
  } else {
    return function (element, event, handler) {
      if (element && event && handler) {
        element.attachEvent('on' + event, handler);
      }
    };
  }
})();

/* istanbul ignore next */
const off = (function () {
  if (
    !(Vue.prototype.$isServer || Number(document.documentMode)) &&
    document.removeEventListener
  ) {
    return function (element, event, handler) {
      if (element && event) {
        element.removeEventListener(event, handler, false);
      }
    };
  } else {
    return function (element, event, handler) {
      if (element && event) {
        element.detachEvent('on' + event, handler);
      }
    };
  }
})();
export default {
  inheritAttrs: false,
  props: {
    // 默认提供的主题，可选值：dark/light
    effect: {
      type: String,
      default: 'dark',
    },
    // 提示文字的内容
    content: {
      type: [String, Number],
      default: '',
    },
    // Tooltip 的出现位置，可选值为:top/bottom/left/right
    placement: {
      type: String,
      default: 'bottom',
    },
    // 状态是否可见
    displayTip: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      placementPosition: 'bottom', // 位置
      placementType: {
        TOP: 'top',
        // TOP_START: 'top-start',
        // TOP_END: 'top-end',
        BOTTOM: 'bottom',
        // BOTTOM_START: 'bottom-start',
        // BOTTOM_END: 'bottom-end',
        LEFT: 'left',
        // LEFT_START: 'left-start',
        // LEFT_END: 'left-end',
        RIGHT: 'right',
        // RIGHT_START: 'right-start',
        // RIGHT_END: 'right-end',
      },
      placementOptions: [],
      displayToolTip: false, // 是否显示Tooltip
      isComputed: false, // 是否计算中
      isEnterSlot: false, // 是否移入Slot
      isEnterTip: false, // 是否移入Tip
      isInited: false, // 是否初始化过
      slotNode: null, // 插槽的第一个Node
      timeout: null, // 计时器
    };
  },
  mounted() {
    this.displayToolTip = this.handleValue(this.displayTip);
    this.formateProps(); // 过滤参数
    this.isInited = true;
  },
  beforeDestroy() {
    off(this.slotNode, 'mouseenter', this.mouseEnter);
    off(this.slotNode, 'mouseleave', this.mouseLeave);
    this.displayToolTip = false;
    this.isComputed = false;
    this.isEnterSlot = false;
    this.isEnterTip = false;
    this.isInited = false;
    this.slotNode = null;
    clearTimeout(this.timeout);
  },
  computed: {},
  watch: {
    displayTip(val) {
      if (this.displayToolTip === val) {
        return;
      }
      this.displayToolTip = this.handleValue(val);
      this.formateProps(); // 过滤参数
    },
  },
  methods: {
    // 根据数据类型进行处理，如果是原始值则直接赋值，如果是引用值则进行克隆
    handleValue(data) {
      if (Object.prototype.toString.call(data) === '[object Array]') {
        return Object.assign([], data);
      } else if (Object.prototype.toString.call(data) === '[object Object]') {
        return Object.assign({}, data);
      } else {
        return data;
      }
    },
    // 过滤参数
    formateProps() {
      if (!this.placementOptions || !this.placementOptions.length) {
        for (let key in this.placementType) {
          this.placementOptions.push(this.placementType[key]);
        }
      }
      if (
        this.placement &&
        this.placementOptions.includes(this.placement) &&
        this.placement !== this.placementPosition
      ) {
        this.placementPosition = this.placement;
      }
      this.initToolTip();
      this.isComputed = false;
    },
    // 初始化tip
    initToolTip() {
      if (this.isComputed) {
        return;
      }
      this.isComputed = true;

      // 获取插槽第一个节点： 方式一
      // let parentDom = this.$refs.toolTipParent;
      // if (!parentDom || !parentDom.childNodes.length) {
      //   console.error('Dom toolTipParent or ChildNodes is not define');
      //   return;
      // }
      // if (!this.slotNode) {
      //   this.slotNode = parentDom.childNodes[0];
      // }

      // 获取插槽第一个节点： 方式二
      if (!this.slotNode) {
        this.slotNode =
          this.$slots.default &&
          this.$slots.default[0] &&
          this.$slots.default[0].elm
            ? this.$slots.default[0].elm
            : null;
      }
      if (!this.slotNode) {
        console.error('Dom content is not define');
        return;
      }
      let tipDom = this.$refs.toolTipDom;
      if (!tipDom) {
        console.error('Dom toolTipDom is not define');
        return;
      }
      // 第一次加载，添加slotNode的鼠标移入、移除监听事件
      if (!this.isInited && this.slotNode) {
        on(this.slotNode, 'mouseenter', this.mouseEnter);
        on(this.slotNode, 'mouseleave', this.mouseLeave);
      }
      // 未显示，不继续执行
      if (!this.displayToolTip) return;
      // 每次显示时，都模拟手动更新并计算，避免插槽的位置、宽高更新了
      this.$nextTick(() => {
        // 参数top: slotNode的顶部偏移值，slotNode的高度，tip的高度
        // 参数left：slotNode的左侧偏移值，slotNode的宽度，tip的宽度
        switch (this.placementPosition) {
          case this.placementType.TOP:
            tipDom.style.top =
              this.slotNode.offsetTop - tipDom.clientHeight - 5 + 'px';
            tipDom.style.left =
              this.slotNode.offsetLeft +
              this.slotNode.offsetWidth / 2 -
              tipDom.offsetWidth / 2 +
              'px';
            break;
          case this.placementType.BOTTOM:
            tipDom.style.top =
              this.slotNode.offsetTop + this.slotNode.clientHeight + 5 + 'px';
            tipDom.style.left =
              this.slotNode.offsetLeft +
              this.slotNode.offsetWidth / 2 -
              tipDom.offsetWidth / 2 +
              'px';
            break;
          case this.placementType.LEFT:
            tipDom.style.top =
              this.slotNode.offsetTop +
              this.slotNode.clientHeight / 2 -
              tipDom.clientHeight / 2 +
              'px';
            tipDom.style.left =
              this.slotNode.offsetLeft - tipDom.offsetWidth - 5 + 'px';
            break;
          case this.placementType.RIGHT:
            tipDom.style.top =
              this.slotNode.offsetTop +
              this.slotNode.clientHeight / 2 -
              tipDom.clientHeight / 2 +
              'px';
            tipDom.style.left =
              this.slotNode.offsetLeft + this.slotNode.offsetWidth + 5 + 'px';
            break;
          default:
            // 默认bottom
            tipDom.style.top =
              this.slotNode.offsetTop + this.slotNode.clientHeight + 5 + 'px';
            tipDom.style.left =
              this.slotNode.offsetLeft +
              this.slotNode.offsetWidth / 2 -
              tipDom.offsetWidth / 2 +
              'px';
            break;
        }
      });
    },
    // 鼠标移入： 插槽的第一个Node
    mouseEnter() {
      this.displayChange(true);
      this.formateProps(); // 过滤参数
    },
    // 鼠标移出： 插槽的第一个Node
    mouseLeave() {
      if (this.isEnterTip) return;
      this.isEnterSlot = false;
      this.timeout = setTimeout(() => {
        if (this.isEnterTip || this.isEnterSlot) return;
        this.displayChange(false);
      }, 200);
    },
    // tip显隐状态改变
    displayChange(booleanVal) {
      this.displayToolTip = booleanVal;
      this.$emit('update:displayTip', booleanVal);
      this.isEnterSlot = booleanVal;
    },
    // 鼠标移入： 提示文字
    mouseEnterTip() {
      this.isEnterTip = true;
    },
    // 鼠标移出： 提示文字
    mouseLeaveTip() {
      this.isEnterTip = false;
      this.isEnterSlot = false;
      this.mouseLeave();
    },
  },
};
</script>

<style lang="less"></style>

<style lang="less" scoped>
.tool-tip-box {
  position: relative;
  display: inline-block;
  .tool-tip-dom {
    position: absolute;
    z-index: 999;
    display: none;
    .tool-tip-content {
      display: inline-block;
      border: 1px solid #ebebeb;
      border-radius: 6px;
      box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2);
      padding: 5px;
      background-color: #ffffff;
      flex-wrap: nowrap;
      white-space: nowrap;
      font-size: 14px;
    }

    .tip-arrow {
      position: absolute;
      border: 7px solid transparent;
    }

    .tip-arrow:after {
      position: absolute;
      content: '';
      width: 0;
      height: 0;
      box-sizing: border-box;
      border: 7px solid transparent;
    }

    .tool-tip-content.top {
      margin-bottom: 7px;
    }

    .tip-arrow.top {
      border-bottom-width: 0;
      bottom: 0;
      left: 50%;
      margin-left: -7px;
      border-top-color: #ebebeb;
    }

    .tip-arrow.top:after {
      margin-left: -7px;
      margin-top: -8px;
      border-bottom-width: 0;
      border-top-color: #fff;
    }

    .tool-tip-content.bottom {
      margin-top: 7px;
    }

    .tip-arrow.bottom {
      border-top-width: 0;
      top: 0;
      left: 50%;
      margin-left: -7px;
      border-bottom-color: #ebebeb;
    }

    .tip-arrow.bottom:after {
      margin-left: -7px;
      margin-top: 1px;
      border-top-width: 0;
      border-bottom-color: #fff;
    }

    .tool-tip-content.left {
      margin-right: 7px;
    }

    .tip-arrow.left {
      border-right-width: 0;
      right: 0;
      top: 50%;
      margin-top: -7px;
      border-left-color: #ebebeb;
    }

    .tip-arrow.left:after {
      margin-left: -8px;
      margin-top: -7px;
      border-right-width: 0;
      border-left-color: #fff;
    }

    .tool-tip-content.right {
      margin-left: 7px;
    }

    .tip-arrow.right {
      border-left-width: 0;
      left: 0;
      top: 50%;
      margin-top: -7px;
      border-right-color: #ebebeb;
    }

    .tip-arrow.right:after {
      margin-left: 1px;
      margin-top: -7px;
      border-left-width: 0;
      border-right-color: #fff;
    }
  }
  .tool-tip-dom-display {
    display: block;
  }
}
.tool-tip-dark {
  .tool-tip-dom {
    .tool-tip-content {
      border-color: #262626;
      background-color: #262626;
      color: #fff;
    }
    .tip-arrow.top {
      border-top-color: #262626;
    }
    .tip-arrow.top:after {
      border-top-color: #262626;
    }
    .tip-arrow.bottom {
      border-bottom-color: #262626;
    }
    .tip-arrow.bottom:after {
      border-bottom-color: #262626;
    }
    .tip-arrow.left {
      border-left-color: #262626;
    }
    .tip-arrow.left:after {
      border-left-color: #262626;
    }
    .tip-arrow.right {
      border-right-color: #262626;
    }
    .tip-arrow.right:after {
      border-right-color: #262626;
    }
  }
}
</style>
